﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;

namespace CommonEngine
{
    public static class SceneExtend
    {
        /// <summary>
        /// 树形数据转化
        /// </summary>
        /// <param name="list">集合数据</param>
        /// <returns>树结构集合</returns>
        public static List<TreeModel> ToTree(this List<TreeModel> list, string parentId = "")
        {
            Dictionary<string, List<TreeModel>> childrenMap = new Dictionary<string, List<TreeModel>>();
            Dictionary<string, TreeModel> parentMap = new Dictionary<string, TreeModel>();
            List<TreeModel> res = new List<TreeModel>();

            //首先按照
            foreach (var node in list)
            {
                node.hasChildren = false;
                node.complete = true;
                // 注册子节点映射表
                if (!childrenMap.ContainsKey(node.parentId))
                {
                    childrenMap.Add(node.parentId, new List<TreeModel>());
                }
                else if (parentMap.ContainsKey(node.parentId))
                {
                    parentMap[node.parentId].hasChildren = true;
                }
                childrenMap[node.parentId].Add(node);
                // 注册父节点映射节点表
                parentMap.Add(node.id, node);

                // 查找自己的子节点
                if (!childrenMap.ContainsKey(node.id))
                {
                    childrenMap.Add(node.id, new List<TreeModel>());
                }
                else
                {
                    node.hasChildren = true;
                }
                node.ChildNodes = childrenMap[node.id];
            }

            if (parentId.IsEmpty())
            {
                // 获取祖先数据列表
                foreach (var item in childrenMap)
                {
                    if (!parentMap.ContainsKey(item.Key))
                    {
                        res.AddRange(item.Value);
                    }
                }
            }
            else
            {
                if (childrenMap.ContainsKey(parentId))
                {
                    return childrenMap[parentId];
                }
                else
                {
                    return new List<TreeModel>();
                }
            }
            return res;
        }

        /// <summary>
        /// 树形数据转化
        /// </summary>
        /// <param name="list">集合数据</param>
        /// <returns>树结构集合</returns>
        public static List<TreeEn<T>> ToTree<T>(this List<TreeEn<T>> list) where T : class
        {
            Dictionary<string, List<TreeEn<T>>> childrenMap = new Dictionary<string, List<TreeEn<T>>>();
            Dictionary<string, TreeEn<T>> parentMap = new Dictionary<string, TreeEn<T>>();
            List<TreeEn<T>> res = new List<TreeEn<T>>();

            //首先按照
            foreach (var node in list)
            {
                // 注册子节点映射表
                if (!childrenMap.ContainsKey(node.parentId))
                {
                    childrenMap.Add(node.parentId, new List<TreeEn<T>>());
                }
                childrenMap[node.parentId].Add(node);
                // 注册父节点映射节点表
                parentMap.Add(node.id, node);

                // 查找自己的子节点
                if (!childrenMap.ContainsKey(node.id))
                {
                    childrenMap.Add(node.id, new List<TreeEn<T>>());
                }
                node.ChildNodes = childrenMap[node.id];
            }
            // 获取祖先数据列表
            foreach (var item in childrenMap)
            {
                if (!parentMap.ContainsKey(item.Key))
                {
                    res.AddRange(item.Value);
                }
            }
            return res;
        }

        #region--VOL--

        public static string ReplacePath(this string path)
        {
            if (string.IsNullOrEmpty(path))
                return "";
            if (_windows)
                return path.Replace("/", "\\");
            return path.Replace("\\", "/");

        }
        private static bool _windows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
        private static DateTime dateStart = new DateTime(1970, 1, 1, 8, 0, 0);
        private static long longTime = 621355968000000000;
        private static int samllTime = 10000000;
        /// <summary>
        /// 获取时间戳 
        /// </summary>
        public static long GetTimeStamp(this DateTime dateTime)
        {
            return (dateTime.ToUniversalTime().Ticks - longTime) / samllTime;
        }
        /// <summary>
        /// 时间戳转换成日期
        /// </summary>
        public static DateTime GetTimeSpmpToDate(this object timeStamp)
        {
            if (timeStamp == null)
                return dateStart;
            DateTime dateTime = new DateTime(longTime + Convert.ToInt64(timeStamp) * samllTime, DateTimeKind.Utc).ToLocalTime();
            return dateTime;
        }
        public static bool IsUrl(this string str)
        {
            if (string.IsNullOrEmpty(str))
                return false;
            string Url = @"(http://)?([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?";
            return Regex.IsMatch(str, Url);
        }
        /// <summary>
        /// 判断是不是正确的手机号码
        /// </summary>
        public static bool IsPhoneNo(this string input)
        {
            if (string.IsNullOrEmpty(input))
                return false;
            if (input.Length < 11)
                return false;

            if (new Regex(@"^1[3578][01379]\d{8}$").IsMatch(input)
                || new Regex(@"^1[34578][01256]\d{8}").IsMatch(input)
                || new Regex(@"^(1[012345678]\d{8}|1[345678][0123456789]\d{8})$").IsMatch(input)
                )
                return true;
            return false;
        }
        /// <summary>
        /// 根据传入格式判断是否为小数
        /// </summary>
        public static bool IsNumber(this string str, string formatString)
        {
            if (string.IsNullOrEmpty(str))
                return false;
            int precision = 32;
            int scale = 5;
            try
            {
                string[] numbers = formatString.Split(',');
                precision = Convert.ToInt32(numbers[0]);
                scale = Convert.ToInt32(numbers[1]);
            }
            catch { };
            return IsNumber(str, precision, scale);
        }
        /// <summary>
        /// 判断一个字符串是否为合法数字(指定整数位数和小数位数)
        /// </summary>
        /// <param name="str">字符串</param>
        /// <param name="precision">整数位数</param>
        /// <param name="scale">小数位数</param>
        /// <returns></returns>
        public static bool IsNumber(this string str, int precision, int scale)
        {
            if ((precision == 0) && (scale == 0))
            {
                return false;
            }
            string pattern = @"(^\d{1," + precision + "}";
            if (scale > 0)
            {
                pattern += @"\.\d{0," + scale + "}$)|" + pattern;
            }
            pattern += "$)";
            return Regex.IsMatch(str, pattern);
        }

        #endregion

        /// <summary>
        /// List转成Tree(重构)
        /// </summary>
        /// <typeparam name="T">实体类型（通常满足递归结构有：Id，ParentId）</typeparam>
        /// <typeparam name="K">Id的基础数据类型（通常为int、string）</typeparam>
        /// <typeparam name="V">树视图类型（有链表属性Children符合递归条件）</typeparam>
        /// <param name="collection">实体集合（顺表集合List，链表应该也适用）</param>
        /// <param name="idSelector">实体对象取Id的方法</param>
        /// <param name="parentIdSelector">实体对象取ParentId的方法</param>
        /// <param name="Maping">实体对象填充试图策略操作</param>
        /// <param name="V_Children">视图类的链表属性</param>
        /// <param name="rootId">根节点的Id值</param>
        /// <returns>链表树</returns>
        public static IEnumerable<V> GenerateTree<T, K, V>(this IEnumerable<T> collection, 
            Func<T, K> idSelector, 
            Func<T, K> parentIdSelector, 
            Func<T, V> Maping, 
            Expression<Func<V, object>> V_Children, 
            K rootId = default(K))
            where T : class
            where V : class
        {
            if (collection == null || Maping == null || V_Children == null)
                return null;

            var items = collection.GenerateTree(idSelector, parentIdSelector, rootId);
            if (items == null)
                return null;

            return MapingTree(items, Maping, V_Children);
        }

        /// <summary>
        /// 将树形改为集合返回
        /// </summary>
        public static List<DataBox<T>> FlattenTree<T>(this IEnumerable<T> TreeLs, 
            Func<T, DataBox> Maping, 
            Expression<Func<T, object>> T_Children, 
            bool HasLadder = true) 
            where T : class
        {
            bool HasChildren = false;
            string Children = GetPropertyName<T>(T_Children);
            Type ChildrenT = GetPropertyType<T>(Children);
            if (ChildrenT.GetInterfaces().Contains(typeof(IEnumerable<T>)))
                HasChildren = true;

            var result = new List<DataBox<T>>();
            var loop = FixedPointFactory.Fix<IEnumerable<T>, int>(f => (Ls, lvl) =>
            {
                if (Ls == null)
                    return;

                Ls.ToList().ForEach(c =>
                {
                    DataBox LI = Maping(c);
                    string Name = LI.Key;
                    string view = LI.Key;
                    if (HasLadder)
                    {
                        if (lvl < 1)
                            view = Name;
                        else if (lvl == 1)
                            view = "|----" + Name;
                        else if (lvl == 2)
                            view = "|----|----" + Name;
                        else if (lvl == 3)
                            view = "|----|----|----" + Name;
                        else if (lvl == 4)
                            view = "|----|----|----|----" + Name;
                        else
                            view = "|----|----|----|----|----" + Name;
                    }

                    DataBox<T> LIT = new DataBox<T>() { Item = c };
                    LIT.Seq = LI.Seq;
                    LIT.ItemID = LI.ItemID;
                    LIT.Key = Name;
                    LIT.View = view;
                    LIT.Val = LI.Val;
                    LIT.level = lvl;
                    LIT.Selected = LI.Selected;

                    result.Add(LIT);

                    if (HasChildren)
                    {
                        var LsT = GetPropertyValue(c, Children) as IEnumerable<T>;
                        f(LsT, lvl + 1);
                    }
                });
            });

            loop(TreeLs, 0);

            return result;
        }

        #region--私有辅助操作--

        private static IEnumerable<TreeItem<T>> GenerateTree<T, K>(this IEnumerable<T> collection, Func<T, K> idSelector, Func<T, K> parentIdSelector, K rootId = default(K)) where T : class
        {
            if (collection == null || idSelector == null || parentIdSelector == null)
                yield return null;

            foreach (var c in collection.Where(u =>
            {
                var selector = parentIdSelector(u);
                return (rootId == null && selector == null)
                || (rootId != null && rootId.Equals(selector));
            }))
            {
                yield return new TreeItem<T>
                {
                    Item = c,
                    Children = collection.GenerateTree(idSelector, parentIdSelector, idSelector(c))
                };
            }
        }
        private static IEnumerable<K> MapingTree<T, K>(IEnumerable<TreeItem<T>> collection, Func<T, K> Maping, Expression<Func<K, object>> K_Children)
            where T : class
            where K : class
        {
            bool HasChildren = false;
            string kName = GetPropertyName<K>(K_Children);
            Type kt = GetPropertyType<K>(kName);
            if (kt.GetInterfaces().Contains(typeof(IEnumerable<K>)))
                HasChildren = true;

            var Map = FixedPointFactory.Fix<TreeItem<T>, Func<IEnumerable<TreeItem<T>>, IEnumerable<K>>, K>(f => (node, fun) =>
            {
                K newNode = Maping(node.Item);
                if (HasChildren)
                    SetPropertyValue(newNode, fun(node.Children), kName);

                return newNode;
            });

            var loop = FixedPointFactory.Fix<IEnumerable<TreeItem<T>>, IEnumerable<K>>(f => (listObj) =>
            {
                if (listObj == null)
                    return null;

                List<K> LsNode = new List<K>();
                listObj.ToList().ForEach(node =>
                {
                    LsNode.Add(Map(node, f));
                });
                return LsNode;
            });

            return loop(collection);
        }

        private static string GetPropertyName<T>(Expression<Func<T, object>> expr) where T : class
        {
            string propertyName = string.Empty;
            if (expr.Body is UnaryExpression)
            {
                propertyName = ((MemberExpression)((UnaryExpression)expr.Body).Operand).Member.Name;
            }
            else if (expr.Body is MemberExpression)
            {
                propertyName = ((MemberExpression)expr.Body).Member.Name;
            }
            else if (expr.Body is ParameterExpression)
            {
                propertyName = ((ParameterExpression)expr.Body).Type.Name;
            }
            return propertyName;
        }
        private static Type GetPropertyType<T>(string propertyName) where T : class
        {
            if (string.IsNullOrEmpty(propertyName))
                return null;

            PropertyInfo ps = typeof(T).GetProperty(propertyName);
            if (ps == null)
                return null;
            else
            {
                Type pt = ps.PropertyType;
                if (pt.IsGenericType && pt.GetGenericTypeDefinition() == typeof(Nullable<>))
                    pt = pt.GetGenericArguments()[0];

                return pt;
            }
        }
        private static object GetPropertyValue(object enValue, string propertyName)
        {
            if (enValue == null)
                return null;

            PropertyInfo[] ps = enValue.GetType().GetProperties();
            foreach (PropertyInfo p in ps)
            {
                if (p.Name == propertyName)
                {
                    return p.GetValue(enValue, null);
                }
            }
            return null;
        }
        private static void SetPropertyValue(object en, object EnValue, string propertyName)
        {
            if (en == null)
                return;

            PropertyInfo[] ps = en.GetType().GetProperties();
            foreach (PropertyInfo p in ps)
            {
                if (p.Name == propertyName)
                {
                    p.SetValue(en, EnValue, null);
                    return;
                }
            }
        }

        #endregion
    }
}
